#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "UISpeechEvent.h"
#include "AudioLogger.h"
#include "VAutoWrapper.h"

#define PCM_BUF_SIZE        0x1000

/* ******************************************************************
**  CALLBACK FUNCTION TO PLAY AUDIO
** ******************************************************************/
static NUAN_ERROR cbOutNotifyToBuffer(
  VE_HINSTANCE              hTTSInst,
  VE_OUTDEV_HINSTANCE       hOutDevInst,
  VE_CALLBACKMSG          * pcbMessage,
  VE_USERDATA               userData) {

	VE_OUTDATA  *pTTSOutData;
	AudioLogger *pAudioLogger = TTSEngine::getTTSAudioLogger();
	
	switch (pcbMessage->eMessage) {
	case VE_MSG_BEGINPROCESS:
		idStatus = 1; //BeginProcess
		break;

	case VE_MSG_ENDPROCESS:
		//playBacker.isPlayingBack = false;
		idStatus = 3; //EndProcess

		if (pAudioLogger)
			pAudioLogger->closeLogFile();
		bool isLocalPlay;
		isLocalPlay = false;
		UIConfigResourceManager::getInstance()->getBoolValue(MODULE_SpeechManager, "tts", "isLocalPlay", isLocalPlay);
		if (isLocalPlay) {
			//TODO: replace sndPlaySound()
		//  sndPlaySound(pAudioLogger->getLogFile().c_str(), SND_SYNC);
			sendSpeechOutboundEvent(pAudioLogger->getSessionID(), -1, EVENT_OUT_TTS_PLAY_DONE, "");
		}
		else {
			UIFile file;
			file.openFileBin(pAudioLogger->getLogFile().c_str());
			int len = file.fileLen();
			if (len > 0) {
				char* data = new char[len];
				file.readBuff(data, len);
				sendSpeechOutboundEvent(
					pAudioLogger->getSessionID(), -1, EVENT_OUT_TTS_VOICE_DATA, 
					serializeTTSVoiceData(0, true, data, len)
				);
			}
		}
		break;

	case VE_MSG_OUTBUFDONE:
		pTTSOutData = (VE_OUTDATA *)pcbMessage->pParam;
		if (pTTSOutData->ulPcmBufLen != 0 && pAudioLogger) {
			pAudioLogger->writeBufferToFile(pTTSOutData->pOutPcmBuf, pTTSOutData->ulPcmBufLen);
		}
		idStatus = 2; //Speaking
		break;

	case VE_MSG_OUTBUFREQ:
		pTTSOutData = (VE_OUTDATA *)pcbMessage->pParam;
		pTTSOutData->pOutPcmBuf = pPcmData;
		pTTSOutData->ulPcmBufLen = PCM_BUF_SIZE;
		break;

	default:
		break;
	}

	return 0;
}

/* ******************************************************************
**  INITIALIZATION
** ******************************************************************/
bool VAutoWrapper::initialize() {
	NUAN_ERROR errCode;

	std::string dataPath;
	std::string voice;

	if (!m_pConfigurator->getFileName(MODULE_SpeechManager, "vauto", "dataDir", dataPath)) {
		m_pLogger->err(MODULE_SpeechManager, "VAutoWrapper", "No dataDir specified.");
		return false;
	}
	if (!m_pConfigurator->getStringValue(MODULE_SpeechManager, "vauto", "voice", voice)) {
		m_pLogger->err(MODULE_SpeechManager, "VAutoWrapper", "No voice specified.");
		return false;
	}

	strcpy(sVoice, voice.c_str());
	strcpy((char *)&dataInstallPath[0], dataPath.c_str());

	cTTSParam = 0;

	// Allocate our PCM and marker buffers
	pPcmData = (NUAN_S16 *) malloc(PCM_BUF_SIZE);
	if (pPcmData == NULL) return false;

	// setup install
	memset(&hInstall, 0, sizeof(hInstall));
	hInstall.fmtVersion = VE_CURRENT_VERSION;

	// setup installation information
	memset(&vResources, 0, sizeof(vResources));
	vResources.fmtVersion = VE_CURRENT_VERSION;
	vResources.u16NbrOfDataInstall = 1;
	gDataInstallPaths[0] = dataInstallPath[0];
	vResources.apDataInstall = (PLATFORM_TCHAR**)&gDataInstallPaths;

	errCode = vplatform_GetInterfaces(&hInstall, &vResources);
	if (errCode != NUAN_OK) {
		free(pPcmData);
		return false;
	}

	// Initialize the engine
	errCode = ve_ttsInitialize(&hInstall, &hSpeech);
	if (errCode != NUAN_OK) {
		free(pPcmData);
		return false;
	}

	// Create the TTS instance
	errCode = ve_ttsOpen(hSpeech, hInstall.hHeap, hInstall.hLog, &hTTSInst, (VE_USERDATA)NULL);
	if (errCode != NUAN_OK) {
		ve_ttsUnInitialize(hSpeech);
		free(pPcmData);
		return false;
	}

	// Initialize params with default values
	memset(ttsParam, 0, sizeof(ttsParam));
	cTTSParam = 0;

	//ttsParam[cTTSParam].eID = VAUTO_PARAM_LANGUAGE_NR;
	//ttsParam[cTTSParam++].uValue.usValue = 10;
	//strcpy(ttsParam[cTTSParam++].uValue.szStringValue, "American English");
	
	//ttsParam[cTTSParam].eID = VAUTO_PARAM_MARKER_MODE;
	//ttsParam[cTTSParam++].uValue.usValue = (NUAN_U16) VAUTO_MRK_OFF; //VAUTO_MRK_ON, VAUTO_MRK_OFF

	//ttsParam[cTTSParam].eID = VAUTO_PARAM_WAITFACTOR;
	//ttsParam[cTTSParam++].uValue.usValue = 1;

	// init mode : no modules loaded
	//ttsParam[cTTSParam].eID = VAUTO_PARAM_INITMODE;
	//ttsParam[cTTSParam++].uValue.usValue = VAUTO_INITMODE_LOAD_ONCE_OPEN_ALL;//VAUTO_INITMODE_LOAD_OPEN_ALL_EACH_TIME  VAUTO_INITMODE_LOAD_ONCE_OPEN_ALL;

	// set voice
	ttsParam[cTTSParam].eID = VE_PARAM_VOICE;
	strcpy(ttsParam[cTTSParam++].uValue.szStringValue, sVoice);

	// set char type
	ttsParam[cTTSParam].eID = VE_PARAM_TYPE_OF_CHAR;
	ttsParam[cTTSParam++].uValue.usValue = VE_TYPE_OF_CHAR_UTF8;

	//cTTSParam=0;
	// Set parameters
	errCode = ve_ttsSetParamList(hTTSInst , &ttsParam[0] , cTTSParam);
	if (errCode != NUAN_OK) {
		unInitialize();
		return false;
	}

	//cTTSParam=0;
	
	// Configure the system to load all modules at startup
	//ttsParam[0].eID = VAUTO_PARAM_INITMODE;
	//ttsParam[0].uValue.usValue = VAUTO_INITMODE_LOAD_ONCE_OPEN_ALL;
	//errCode = vauto_ttsSetParamList(hTTSInst , &ttsParam[0] , 1);
	//if (errCode != NUAN_OK) {
	//    unInit();
	//    return false;
	//}

	// Set the output device
	memset(&hOutDevInfo, 0, sizeof(hOutDevInfo));
	hOutDevInfo.hOutDevInstance = (VE_OUTDEV_HINSTANCE) pPcmData;
	hOutDevInfo.pfOutNotify = cbOutNotifyToBuffer;

	errCode = ve_ttsSetOutDevice(hTTSInst, &hOutDevInfo);
	if (errCode != NUAN_OK) {
		unInitialize();
		return false;
	}

	// setup default status
	idStatus = 0;

	return true;
}

void VAutoWrapper::unInitialize() {
	ve_ttsClose(hTTSInst);
	ve_ttsUnInitialize(hSpeech);
	free(pPcmData);
	//free(hInstall.hHeap);
	//playBacker.unInit();
}

bool VAutoWrapper::loadUserDict(const char * fName) {
//	NUAN_ERROR errCode;
//	NUAN_U32 nResLen;

	// Read the input resource
   /* nResLen = readTextFromFile(fName, pDict);

	if (nResLen == 0) return false;

	errCode = vauto_ttsResourceLoad(hTTSInst, "application/edct-bin-dictionary", nResLen, pDict, &hDcit);
	if (errCode != NUAN_OK) return false;*/

	return true;
}

bool VAutoWrapper::unLoadUserDict() {
	/*NUAN_ERROR errCode;
	errCode = vauto_ttsResourceUnload(hTTSInst, hDcit);
	if (errCode != NUAN_OK) return false;

	free(pDict);*/
	return true;
}

bool VAutoWrapper::speak(int sessionID, const char * promptString, const char *logFilePath) {
	NUAN_ERROR errCode;
	VE_INTEXT inText;

	AudioLogger* pAudioLogger = TTSEngine::getTTSAudioLogger();
	if (pAudioLogger)
		pAudioLogger->createLogFile(logFilePath, sessionID);

	// Prepare text string for speaking
	inText.eTextFormat  = VE_NORM_TEXT;
	inText.ulTextLength = (NUAN_U32) strlen(promptString);
	inText.szInText     = (void *) promptString;

	// Synthesis
	errCode = ve_ttsProcessText2Speech(hTTSInst, &inText);
	if (errCode != NUAN_OK) return false;

	return true;
}

bool VAutoWrapper::checkPlaybackStopped() {
	//if (playBacker.isStopped) idStatus = 0;
	//printf("check stop %d\n",playBacker.isStopped);
	return true; //playBacker.isStopped;
}

bool VAutoWrapper::stop() {
    //int ret;

	ve_ttsStop(hTTSInst);
	// check to make sure PCM playing back is also stopped.
	//while(!checkPlaybackStopped()) Sleep(10);
	//ret = playBacker.drop();
	//printf("drop ret %d\n",ret);
	idStatus = 0;
	return true;
}

int VAutoWrapper::getStatus() {
	return idStatus;
}

